#include "playthread.h"
#include <SDL2/SDL.h>
#include <QFile>

/* 一些宏定义 */
// 采样率
#define SAMPLE_RATE 44100
// 采样格式
#define SAMPLE_FORMAT AUDIO_S16LSB
// 采样大小
#define SAMPLE_SIZE SDL_AUDIO_BITSIZE(SAMPLE_FORMAT)
// 声道数
#define CHANNELS 2
// 音频缓冲区的样本数量
#define SAMPLES 1024

#ifdef Q_OS_WIN
    #define FILENAME "../test/in.wav"
#else
    #define FILENAME "/Users/zuojie/Desktop/in.pcm"
#endif

// 每个样本占用多少个字节
#define BYTES_PER_SAMPLE ((SAMPLE_SIZE * CHANNELS) >> 3)
// 文件缓冲区的大小，例如：4096
#define BUFFER_SIZE (SAMPLES * BYTES_PER_SAMPLE)

// 用于存储读取的音频数据和长度
typedef struct{
    int len;
    int pullLen = 0;//缓存区每次拉取的数据
    Uint8 * data = nullptr;
}AudioBuffer;

PlayThread::PlayThread(QObject *parent) : QThread(parent){
    connect(this,&PlayThread::finished,&PlayThread::deleteLater);
}

PlayThread::~PlayThread(){
    disconnect();
    requestInterruption();
    quit();
    wait();
    qDebug() << this << "析构了";
}

//int bufferLen;
//char *bufferData;

/**
 * @brief pullAudioData
 * @param userdata SDL_AudioSpec.userdata
 * @param stream 音频缓冲区（需要将音频数据填充到这个缓冲区）
 * @param len 音频缓冲区的大小（SDL_AudioSpec.samples * 每个样本的大小）
 */
void pullAudioData(void *userdata,
                   // 需要往stream中填充PCM数据
                   Uint8 * stream,
                   // 希望填充的大小(samples * format * channels / 8)
                   int len){
    // 清空stream
    SDL_memset(stream, 0, len);

    // 取出缓冲信息
    AudioBuffer *buffer = (AudioBuffer *) userdata;
    // 文件数据还没准备好
    if (buffer->len <= 0) return;

    // 取len、bufferLen的最小值（为了保证数据安全，防止指针越界）
    buffer->pullLen = (len > buffer->len) ? buffer->len : len;

    // 填充数据
    SDL_MixAudio(stream,buffer->data,buffer->pullLen,SDL_MIX_MAXVOLUME);
    buffer->data += buffer->pullLen;
    buffer->len -= buffer->pullLen;
}

/*
SDL播放音频有2种模式：
Push（推）：【程序】主动推送数据给【音频设备】
Pull（拉）：【音频设备】主动向【程序】拉取数据 -- 采用这种
*/
void PlayThread::run(){
    // 初始化Audio子系统
    if(SDL_Init(SDL_INIT_AUDIO) < 0){
        SDL_Log("Unable to initialize SDL: %s", SDL_GetError());
        return;
    }

    // WAV中的PCM数据
    Uint8 *data;
    // WAV中的PCM数据大小（字节）
    Uint32 len;
    // 音频参数
    SDL_AudioSpec spec;

    if(!SDL_LoadWAV(FILENAME,&spec,&data,&len)){
        qDebug() << "SDL_LoadWAV error:" << SDL_GetError();
        // 清除所有的子系统
        SDL_Quit();
        return;
    }

    spec.callback = pullAudioData;
    // 传递给回调函数的userdata
    AudioBuffer buffer;
    buffer.len = len;
    buffer.data = data;
    spec.userdata = &buffer;

    if(SDL_OpenAudio(&spec,nullptr)){
        qDebug() << "SDL_OpenAudio error:" << SDL_GetError();
        // 释放文件数据
        SDL_FreeWAV(data);
        // 清除所有的子系统
        SDL_Quit();
        return;
    }

    // 开始播放(0是取消暂停)
    SDL_PauseAudio(0);

    // 计算一些参数
    int sampleSize = SDL_AUDIO_BITSIZE(spec.format);
    // 每一个样本的大小
    int bytesPerSample = (sampleSize * spec.channels) >> 3;

    while (!isInterruptionRequested()) {
        // 只要从文件中读取的音频数据，还没有填充完毕，就跳过
        if(buffer.len > 0) continue;

        // 文件数据已经读取完毕
        if (buffer.len <= 0) {
           // 最后一次播放的样本数量
           int samples = buffer.pullLen / bytesPerSample;
           // 最后一次播放的时长
           int ms = samples * 1000 / spec.freq;
           SDL_Delay(ms);
           break;
        }
    }

    // 释放WAV文件数据
    SDL_FreeWAV(data);

    // 关闭设备
    SDL_CloseAudio();

    // 清除所有的子系统
    SDL_Quit();
}
